From 396acf3bbbe00a192cb0ea0a9ccf91b1d8d2850b Mon Sep 17 00:00:00 2001 From: Fuwn <50817549+Fuwn@users.noreply.github.com> Date: Sat, 24 Jan 2026 13:09:50 +0000 Subject: Initial commit Created from https://vercel.com/new --- .../[websiteId]/events/EventProperties.tsx | 127 +++++++++++++++++++++ .../[websiteId]/events/EventsDataTable.tsx | 48 ++++++++ .../[websiteId]/events/EventsMetricsBar.tsx | 40 +++++++ .../websites/[websiteId]/events/EventsPage.tsx | 59 ++++++++++ .../websites/[websiteId]/events/EventsTable.tsx | 107 +++++++++++++++++ .../(main)/websites/[websiteId]/events/page.tsx | 12 ++ 6 files changed, 393 insertions(+) create mode 100644 src/app/(main)/websites/[websiteId]/events/EventProperties.tsx create mode 100644 src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx create mode 100644 src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx create mode 100644 src/app/(main)/websites/[websiteId]/events/EventsPage.tsx create mode 100644 src/app/(main)/websites/[websiteId]/events/EventsTable.tsx create mode 100644 src/app/(main)/websites/[websiteId]/events/page.tsx (limited to 'src/app/(main)/websites/[websiteId]/events') diff --git a/src/app/(main)/websites/[websiteId]/events/EventProperties.tsx b/src/app/(main)/websites/[websiteId]/events/EventProperties.tsx new file mode 100644 index 0000000..c3b1325 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/events/EventProperties.tsx @@ -0,0 +1,127 @@ +import { Column, Grid, ListItem, Select } from '@umami/react-zen'; +import { useMemo, useState } from 'react'; +import { PieChart } from '@/components/charts/PieChart'; +import { LoadingPanel } from '@/components/common/LoadingPanel'; +import { + useEventDataPropertiesQuery, + useEventDataValuesQuery, + useMessages, +} from '@/components/hooks'; +import { ListTable } from '@/components/metrics/ListTable'; +import { CHART_COLORS } from '@/lib/constants'; + +export function EventProperties({ websiteId }: { websiteId: string }) { + const [propertyName, setPropertyName] = useState(''); + const [eventName, setEventName] = useState(''); + + const { formatMessage, labels } = useMessages(); + const { data, isLoading, isFetching, error } = useEventDataPropertiesQuery(websiteId); + + const events: string[] = data + ? data.reduce((arr: string | any[], e: { eventName: any }) => { + return !arr.includes(e.eventName) ? arr.concat(e.eventName) : arr; + }, []) + : []; + const properties: string[] = eventName + ? data?.filter(e => e.eventName === eventName).map(e => e.propertyName) + : []; + + return ( + + + {data && ( + + + + + )} + {eventName && propertyName && ( + + )} + + + ); +} + +const EventValues = ({ websiteId, eventName, propertyName }) => { + const { + data: values, + isLoading, + isFetching, + error, + } = useEventDataValuesQuery(websiteId, eventName, propertyName); + + const propertySum = useMemo(() => { + return values?.reduce((sum, { total }) => sum + total, 0) ?? 0; + }, [values]); + + const chartData = useMemo(() => { + if (!propertyName || !values) return null; + return { + labels: values.map(({ value }) => value), + datasets: [ + { + data: values.map(({ total }) => total), + backgroundColor: CHART_COLORS, + borderWidth: 0, + }, + ], + }; + }, [propertyName, values]); + + const tableData = useMemo(() => { + if (!propertyName || !values || propertySum === 0) return []; + return values.map(({ value, total }) => ({ + label: value, + count: total, + percent: 100 * (total / propertySum), + })); + }, [propertyName, values, propertySum]); + + return ( + + {values && ( + + + + + )} + + ); +}; diff --git a/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx new file mode 100644 index 0000000..f686b3f --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx @@ -0,0 +1,48 @@ +import { type ReactNode, useState } from 'react'; +import { DataGrid } from '@/components/common/DataGrid'; +import { useMessages, useWebsiteEventsQuery } from '@/components/hooks'; +import { FilterButtons } from '@/components/input/FilterButtons'; +import { EventsTable } from './EventsTable'; + +export function EventsDataTable({ + websiteId, +}: { + websiteId?: string; + teamId?: string; + children?: ReactNode; +}) { + const { formatMessage, labels } = useMessages(); + const [view, setView] = useState('all'); + const query = useWebsiteEventsQuery(websiteId, { view }); + + const buttons = [ + { + id: 'all', + label: formatMessage(labels.all), + }, + { + id: 'views', + label: formatMessage(labels.views), + }, + { + id: 'events', + label: formatMessage(labels.events), + }, + ]; + + const renderActions = () => { + return ; + }; + + return ( + + {({ data }) => } + + ); +} diff --git a/src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx b/src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx new file mode 100644 index 0000000..a7ed399 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx @@ -0,0 +1,40 @@ +import { LoadingPanel } from '@/components/common/LoadingPanel'; +import { useMessages } from '@/components/hooks'; +import { useWebsiteSessionStatsQuery } from '@/components/hooks/queries/useWebsiteSessionStatsQuery'; +import { MetricCard } from '@/components/metrics/MetricCard'; +import { MetricsBar } from '@/components/metrics/MetricsBar'; +import { formatLongNumber } from '@/lib/format'; + +export function EventsMetricsBar({ websiteId }: { websiteId: string }) { + const { formatMessage, labels } = useMessages(); + const { data, isLoading, isFetching, error } = useWebsiteSessionStatsQuery(websiteId); + + return ( + + {data && ( + + + + + + + )} + + ); +} diff --git a/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx b/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx new file mode 100644 index 0000000..55ec040 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx @@ -0,0 +1,59 @@ +'use client'; +import { Column, Tab, TabList, TabPanel, Tabs } from '@umami/react-zen'; +import { type Key, useState } from 'react'; +import { SessionModal } from '@/app/(main)/websites/[websiteId]/sessions/SessionModal'; +import { WebsiteControls } from '@/app/(main)/websites/[websiteId]/WebsiteControls'; +import { Panel } from '@/components/common/Panel'; +import { useMessages } from '@/components/hooks'; +import { EventsChart } from '@/components/metrics/EventsChart'; +import { MetricsTable } from '@/components/metrics/MetricsTable'; +import { getItem, setItem } from '@/lib/storage'; +import { EventProperties } from './EventProperties'; +import { EventsDataTable } from './EventsDataTable'; + +const KEY_NAME = 'umami.events.tab'; + +export function EventsPage({ websiteId }) { + const [tab, setTab] = useState(getItem(KEY_NAME) || 'chart'); + const { formatMessage, labels } = useMessages(); + + const handleSelect = (value: Key) => { + setItem(KEY_NAME, value); + setTab(value); + }; + + return ( + + + + handleSelect(key)}> + + {formatMessage(labels.chart)} + {formatMessage(labels.activity)} + {formatMessage(labels.properties)} + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx new file mode 100644 index 0000000..7fb2eb4 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx @@ -0,0 +1,107 @@ +import { + Button, + DataColumn, + DataTable, + type DataTableProps, + Dialog, + DialogTrigger, + Icon, + IconLabel, + Popover, + Row, + Text, +} from '@umami/react-zen'; +import Link from 'next/link'; +import { Avatar } from '@/components/common/Avatar'; +import { DateDistance } from '@/components/common/DateDistance'; +import { TypeIcon } from '@/components/common/TypeIcon'; +import { useFormat, useMessages, useNavigation } from '@/components/hooks'; +import { Eye, FileText } from '@/components/icons'; +import { EventData } from '@/components/metrics/EventData'; +import { Lightning } from '@/components/svg'; + +export function EventsTable(props: DataTableProps) { + const { formatMessage, labels } = useMessages(); + const { updateParams } = useNavigation(); + const { formatValue } = useFormat(); + + return ( + + + {(row: any) => { + return ( + + + : } + label={formatMessage(row.eventName ? labels.triggeredEvent : labels.viewedPage)} + /> + + + {row.eventName || row.urlPath} + + {row.hasData > 0 && } + + ); + }} + + + {(row: any) => { + return ( + + + + ); + }} + + + {(row: any) => ( + + {row.city ? `${row.city}, ` : ''} {formatValue(row.country, 'country')} + + )} + + + {(row: any) => ( + + {formatValue(row.browser, 'browser')} + + )} + + + {(row: any) => ( + + {formatValue(row.device, 'device')} + + )} + + + {(row: any) => } + + + ); +} + +const PropertiesButton = props => { + return ( + + + + + + + + + ); +}; diff --git a/src/app/(main)/websites/[websiteId]/events/page.tsx b/src/app/(main)/websites/[websiteId]/events/page.tsx new file mode 100644 index 0000000..d77ba3b --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/events/page.tsx @@ -0,0 +1,12 @@ +import type { Metadata } from 'next'; +import { EventsPage } from './EventsPage'; + +export default async function ({ params }: { params: Promise<{ websiteId: string }> }) { + const { websiteId } = await params; + + return ; +} + +export const metadata: Metadata = { + title: 'Events', +}; -- cgit v1.2.3